// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rbchat.sqlite;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;

import com.eva.epc.common.util.CommonUtils;
import com.x52im.rbchat.logic.alarm.AlarmType;
import com.x52im.rbchat.logic.alarm.model.AlarmDto;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

/**
 * 本系统的sqlLite数据表——首页”消息“历史记录的辅助操作实现类
 * （支持同一手机上切换不同的账号使用而不发生数据混乱）.
 * 
 * <p>
 * 本db表中，的业务主键是："key1(非必须) + key2(非必须) + key3(非必须) + message_type"的联合主键.
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @since 2.5
 */
public class AlarmsHistoryTable extends TableRoot
{
	private final static String TAG = AlarmsHistoryTable.class.getSimpleName();

	// 默认的更新时间字段的sqlite SQL时间戳函数
	// 补充说明1：date('now')或CURRENT_TIMESTAMP SQL时间函数是以格林尼治标准时间为基准的，因此在中国使用的话会正好早8个小时
	// 补充说明2：sqlite中的字段类型是弱类型，TIMESTAMP类型字段的使用和读取按String类型即可，请注意使用 SimpleDateFormat 转换
	// 补充说明3："datetime('now', 'localtime')"函数返回的时间只精确到秒，因而不适合进行精确排序时，这个一定要注意！
	public static final String DEFAULT_UPDATE_TIME_FUNCTION = "(datetime('now', 'localtime'))";
	// COLUMN_KEY_UPDATE$TIME 字段的读取和更新时的SimpleDateFormat日期字串格式
	public static final String UPDATE_TIME_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
	
	/** 表格字段名：自增id（主键）（默认ident列，无需插入数据）*/
	private static final String COLUMN_KEY_ID="_id";
	/** 表格字段名：本地数据所有者账号uid（联合主键之首要条件）*/
	private static final String COLUMN_KEY_ACOUNT$UID="_acount_uid";

	/** 表格字段名：对应首页"消息"列表item的类型，@see {@link AlarmDto} */
	public static final String COLUMN_KEY_ALARM$TYPE="alarmType";
	/**
	 * 表格字段名：对应首页"消息"列表item数据的id：
	 *  1）当是好友消息alarm时，此字段存放的是发送方的uid；
	 *  2）当是陌生人消息alarm时，此字段存放的是发送方的uid；
	 *  3）当是普通群聊消息alarm时，此字段存放的是该群的id（即gid）。 */
	public static final String COLUMN_KEY_DATA$ID="dataId";

	/** 表格字段名：对应首页"消息"列表item标题，@see {@link AlarmDto} */
	public static final String COLUMN_KEY_TITLE="title";
	/** 表格字段名：对应首页"消息"列表item内容，@see {@link AlarmDto} */
	public static final String COLUMN_ALARM$CONTENT ="alarm_content";
	/** * 表格字段名：对应首页"消息"列表item时间，@see {@link AlarmDto}，（此字段值目前仅用于UI显示，不作它用）。 */
	public static final String COLUMN_DATE="date";
	/** 表格字段名：对应首页"消息"列表item未读数，@see {@link AlarmDto}的同名列 */
	public static final String COLUMN_FLAG$NUM="flag_num";

	/**
	 * 表格字段名：对应首页"消息"列表item数据的扩展字段1，@see {@link AlarmDto}的同名列
	 *
	 * @deprecated 自v8.0起，本字段已废弃，将在下一版本移除，请勿使用！
	 */
	public static final String COLUMN_EXTRA$STRING1="extra_string1";

	/**
	 * 表格字段名：@see {@link AlarmDto#setAlwaysTop(boolean)}字段。
	 * 本字段中存放的是首页"消息"设置置顶时的标识，本字段不为空且值为"1"时即表示需要置顶，否则不需要置顶。
	 * @since 4.3 */
	public static final String COLUMN_IS$ALWAYS$TOP ="is_always_top";

	/**
	 * 本字段不用作任何UI显示，仅用于记录排序时使用。
	 * 本字段会在每个item数据插入、item的数据内容更新、置顶标识更新时，更新为当前的系统时间戳。 */
	private static final String COLUMN_KEY_UPDATE$TIME ="_update_time";
	
	/** 存放于sqlLite数据库中的表名 */
	public static final String TABLE_NAME="alarms_history";
	
	/** 建表语句 */
	public static final String DB_CREATE="CREATE TABLE "
										+TABLE_NAME+" ( "	
										+COLUMN_KEY_ID+" INTEGER PRIMARY KEY AUTOINCREMENT,"
										+COLUMN_KEY_ACOUNT$UID+" TEXT  ,"
										+ COLUMN_KEY_ALARM$TYPE +" TEXT,"
										+ COLUMN_KEY_DATA$ID +" TEXT  ,"
										+COLUMN_KEY_TITLE+" TEXT,"
										+ COLUMN_ALARM$CONTENT +" TEXT,"
										+COLUMN_DATE+" INTEGER,"
										+COLUMN_FLAG$NUM+" TEXT," 
										+COLUMN_EXTRA$STRING1+" TEXT,"
										+COLUMN_IS$ALWAYS$TOP +" TEXT,"

										+COLUMN_KEY_UPDATE$TIME +" TIMESTAMP default "+DEFAULT_UPDATE_TIME_FUNCTION
										+")";
	//singleton
	private static AlarmsHistoryTable instance;
	
	private AlarmsHistoryTable(Context context)
	{
		super(context);
	}
	
	public static AlarmsHistoryTable getInstance(Context context)
	{
		if(instance==null)
			instance=new AlarmsHistoryTable(context);
		return instance;		
	}


	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 查询数据 START
	/**
	 * 从本地sqlLite的表中查询所需数据.
	 * 
	 * @param acountUidOfOwner 本地数据的所有者账号，本条件是读取本地数据的先决条件，否则就窜数据了！
	 * @param condition 查询条件
	 * @return 游标结果集
	 */
	private Cursor queryHistoryImpl(String acountUidOfOwner, String condition)
	{
		return query(new String[]{
				COLUMN_KEY_ALARM$TYPE
				,COLUMN_KEY_DATA$ID
				,COLUMN_KEY_TITLE
				,COLUMN_ALARM$CONTENT
				,COLUMN_DATE
				,COLUMN_FLAG$NUM
				,COLUMN_EXTRA$STRING1
				,COLUMN_IS$ALWAYS$TOP
				}, COLUMN_KEY_ACOUNT$UID+"='"+acountUidOfOwner+"'"+(condition == null?"":" and "+condition));
	}

	/**
	 * 返回所有的历史Alarm记录.
	 * 
	 * @param findHistotyType 查询条件，比如APP启动时加载数据，要把置顶和非置顶的数据区分出来，就要分两次查询了（因为1次SQL查询解决不了排序问题）
	 * @return
	 */
	public ArrayList<AlarmDto> findHistory(FindHistotyType findHistotyType)
	{
		ArrayList<AlarmDto> cpList= new ArrayList<AlarmDto>();

		// 本地数据的所有者账号，本条件是读取本地数据的先决条件，否则就窜数据了！
		String acountUidOfOwner =  getLocalUserUid();
		if(acountUidOfOwner == null)
			return cpList;

		// 根据查询类型来决定最终的SQL查询条件
		String condition = "";
		switch(findHistotyType)
		{
			// 仅查询置顶的记录
			case OnlyAlwaysTopRecords:
				condition = " "+COLUMN_IS$ALWAYS$TOP+" is not null order by "
//						+COLUMN_KEY_UPDATE$TIME // 20190516注：因本字段值由datetime('now', 'localtime')函数生成，但它不能精确到毫秒，因而不适合参与精确排序
						+COLUMN_DATE
						+" asc";
				break;
			// 仅查询未置顶的记录
			case OnlyNotAlwaysTopRecords:
				condition = " "+COLUMN_IS$ALWAYS$TOP+" is null order by "
//						+COLUMN_KEY_UPDATE$TIME // 20190516注：因本字段值由datetime('now', 'localtime')函数生成，但它不能精确到毫秒，因而不适合参与精确排序
						+COLUMN_DATE
						+" asc";
				break;
			// 查询所有记录
			case IncludeAll:
				condition = " 1=1 order by "
//						+COLUMN_KEY_UPDATE$TIME // 20190516注：因本字段值由datetime('now', 'localtime')函数生成，但它不能精确到毫秒，因而不适合参与精确排序
						+COLUMN_DATE
						+" asc";
				break;
		}

		//条件是 none
		Cursor mCursor = queryHistoryImpl(acountUidOfOwner, condition);
		
		mCursor.moveToFirst();
		while(!mCursor.isAfterLast())
		{
			try
			{
				AlarmDto cp = new AlarmDto();
				cp.setAlarmType(CommonUtils.getIntValue(mCursor.getString(0), AlarmType.undefine));
				cp.setDataId(mCursor.getString(1));
				cp.setTitle(mCursor.getString(2));
				cp.setAlarmContent(mCursor.getString(3));
//				cp.setDate(mCursor.getString(3));
                cp.setDate(mCursor.getLong(4));
				cp.setFlagNum(mCursor.getString(5));
				
				// 扩展字段
				String extraString1 = mCursor.getString(6);
//				cp.setExtraString1(extraString1);

				// 是否置顶标识
				String alwaysTopStr = mCursor.getString(7);
				cp.setAlwaysTop("1".equals(alwaysTopStr));
				
				cpList.add(cp);

				Log.i(TAG, cp.toString());
			}
			catch (Exception e)
			{
				Log.e(TAG, e.getMessage(), e);
			}
			
			mCursor.moveToNext();
		}
		mCursor.close();
		return cpList;
	}
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 查询数据 END


	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 插入数据 START
	/**
	 * 插入一行一对一临时聊天(即陌生人聊天)的首页消息数据.
	 * 
	 * @param amd
	 * @return
	 */
	public long insertHistory(AlarmDto amd)
	{
		// 本地数据的所有者账号，本条件是读取本地数据的先决条件，否则就窜数据了！
		String acountUidOfOwner =  getLocalUserUid();
		if(acountUidOfOwner == null)
			return -1;

		if(amd != null)
		{
			return insertHistory(acountUidOfOwner
						, String.valueOf(amd.getAlarmType())
						, amd.getDataId()
						, amd.getTitle()
						, amd.getAlarmContent()
						, amd.getDate()
						, amd.getFlagNum()
//						, amd.getExtraString1()
					);
		}
		return -1;
	}

	/**
	 * 插入一行数据到表中.
	 * 
	 * @param acountUidOfOwner 本地数据的所有者账号，本条件是读取本地数据的先决条件，否则就窜数据了！
	 * @param alarmType
	 * @param title
	 * @param alarmContent
	 * @param date
	 * @param flagNum
	 * @return the row ID of the newly inserted row, or -1 if an error occurred
	 */
	private long insertHistory(String acountUidOfOwner
				, String alarmType
				, String dataId
				, String title
				, String alarmContent
				, long date
				, String flagNum
//				, String extraString1
			)
	{
		ContentValues initialValues=new ContentValues();
		initialValues.put(COLUMN_KEY_ACOUNT$UID, acountUidOfOwner);
		initialValues.put(COLUMN_KEY_ALARM$TYPE, alarmType);
		initialValues.put(COLUMN_KEY_DATA$ID, dataId);
		initialValues.put(COLUMN_KEY_TITLE, title);
		initialValues.put(COLUMN_ALARM$CONTENT, alarmContent);
		initialValues.put(COLUMN_DATE, date);
		initialValues.put(COLUMN_FLAG$NUM, flagNum);
//		initialValues.put(COLUMN_EXTRA$STRING1, extraString1);
		
//		// 无条件更新数据库中用于排序的”最新修改时间“字段（INSERT时利用SQL的函数作为默认值自动填入日期时间值）
//		initialValues.put(COLUMN_KEY_UPDATE$TIME, DEFAULT_UPDATE_TIME_FUNCTION);
		
		return super.insert(TABLE_NAME, null, initialValues);//
	}
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 插入数据 END


	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 更新数据内容 START
	/**
	 * 更新一行聊天(即陌生人聊天)的首页消息数据.
	 * 
	 * @param amd
	 * @return
	 */
	public long updateHistory(AlarmDto amd)
	{
		// 本地数据的所有者账号，本条件是读取本地数据的先决条件，否则就窜数据了！
		String acountUidOfOwner =  getLocalUserUid();
		if(acountUidOfOwner == null)
			return -1;

		if(amd != null)
		{
			return updateHistory(acountUidOfOwner
					, amd.getTitle()
					, amd.getAlarmContent()
					, amd.getDate()
					, amd.getFlagNum()
//					, amd.getExtraString1()
					, amd.isAlwaysTop()
					
					// 更新条件
					, String.valueOf(amd.getAlarmType())	// 更新条件：”alarm类型“
					, amd.getDataId()						// 更新条件：dataId
					);
		}
		return -1;
	}

	private long updateHistory(String acountUidOfOwner
			, String newtitle
			, String newmsgContent
			, long newdate//String newdate
			, String newflagNum
//			, String newextraString1
			, boolean isAlwaysTop
			, String messageType
			, String dataId)
	{
		ContentValues updateValues=new ContentValues();
		updateValues.put(COLUMN_KEY_TITLE, newtitle);
		updateValues.put(COLUMN_ALARM$CONTENT, newmsgContent);
		updateValues.put(COLUMN_DATE, newdate);
		updateValues.put(COLUMN_FLAG$NUM, newflagNum);
//		updateValues.put(COLUMN_EXTRA$STRING1, newextraString1);
		updateValues.put(COLUMN_IS$ALWAYS$TOP, isAlwaysTop?"1":null);
		// 无条件更新数据库中用于排序的”最新修改时间“字段
		updateValues.put(COLUMN_KEY_UPDATE$TIME
				// Android的sqlite更新API中，不支持使用SQL日期时间函数作为字段值（真这么用则它会错误地被当成字串）
//				, DEFAULT_UPDATE_TIME_FUNCTION
				// 因为sqlite的字段弱类型，对于TIMESTAMP这样的字段，只能自已来格式化日期了
				, new SimpleDateFormat(UPDATE_TIME_DATE_PATTERN).format(new Date())
		);
		
		// 附加更新条件
		String where = constructUpdateCondition(acountUidOfOwner, messageType, dataId);
		return super.update(TABLE_NAME, updateValues, where);
	}

	/**
	 * 组织本SQLite表的行记录更新条件SQL语句。
	 *
	 * @param acountUidOfOwner
	 * @param alarmType
	 * @return
	 */
	private String constructUpdateCondition(String acountUidOfOwner, String alarmType, String dataId)
	{
		// 附加更新条件
		String where = COLUMN_KEY_ACOUNT$UID+"='"+acountUidOfOwner +"'" +
				(alarmType == null?"":" and "+ COLUMN_KEY_ALARM$TYPE +"='"+alarmType+"'") +
				(dataId == null?"":" and "+ COLUMN_KEY_DATA$ID +"='"+dataId+"'"); // 本地数据所属账号+Alarm类型+dataId是首要条件
		return where;
	}
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 更新数据内容 END

	/**
	 * 更新是否置顶标识.
	 *
	 * @param amd
	 * @return
	 */
	public long updateAlwaysTop(AlarmDto amd)
	{
		// 本地数据的所有者账号，本条件是读取本地数据的先决条件，否则就窜数据了！
		String acountUidOfOwner =  getLocalUserUid();
		if(acountUidOfOwner == null)
			return -1;

		if(amd != null)
		{
			return updateAlwaysTop(acountUidOfOwner
					, amd.isAlwaysTop()

					// 更新条件
					, String.valueOf(amd.getAlarmType())	// 更新条件：”alarm类型“
					, amd.getDataId()					    // 更新条件：dataId——消息发送者的uid或者群聊发生的群id等
			);
		}
		return -1;
	}

	private long updateAlwaysTop(String acountUidOfOwner, boolean isAlwaysTop, String alarmType, String dataId)
	{
		ContentValues updateValues=new ContentValues();
		updateValues.put(COLUMN_IS$ALWAYS$TOP, isAlwaysTop?"1":null);
		// 无条件更新数据库中用于排序的”最新修改时间“字段
		updateValues.put(COLUMN_KEY_UPDATE$TIME
				// Android的sqlite更新API中，不支持使用SQL日期时间函数作为字段值（真这么用则它会错误地被当成字串）
				//				, DEFAULT_UPDATE_TIME_FUNCTION
				// 因为sqlite的字段弱类型，对于TIMESTAMP这样的字段，只能自已来格式化日期了
				, new SimpleDateFormat(UPDATE_TIME_DATE_PATTERN).format(new Date())
		);

		// 附加更新条件
		String where = constructUpdateCondition(acountUidOfOwner, alarmType, dataId);

		return super.update(TABLE_NAME, updateValues, where);
	}

	/**
	 * 无差别更新当前账号下所有的未读数为0.
	 *
	 * @return
	 */
	public long clearAllUnread()
	{
		// 本地数据的所有者账号，本条件是读取本地数据的先决条件，否则就窜数据了！
		String acountUidOfOwner =  getLocalUserUid();
		if(acountUidOfOwner == null)
			return -1;

		ContentValues updateValues=new ContentValues();
		updateValues.put(COLUMN_FLAG$NUM, "0");

		// 附加更新条件
		String where = constructUpdateCondition(acountUidOfOwner, null, null);

		return super.update(TABLE_NAME, updateValues, where);
	}

    /**
     * 删除一行首页”消息“.
     *
     * @return
     */
    public long deleteHistory(int alarmType, String dataId)
    {
		String localUid = getLocalUserUid();
		if(localUid == null)
			return 0;

		// 本地数据的所有者账号，本条件是读取本地数据的先决条件，否则就窜数据了！
		String acountUidOfOwner = localUid;

        return super.delete(TABLE_NAME
                // 指定消息发送者的本地记录
                , COLUMN_KEY_ACOUNT$UID+"='"+acountUidOfOwner
                        +"' and "+ COLUMN_KEY_ALARM$TYPE +"='"+alarmType
                        +"' and "+ COLUMN_KEY_DATA$ID +"='"+dataId+"'");
    }




	public String getTableName()
	{
		return TABLE_NAME;
	}

	/**
	 * 数据查询类型枚举。
	 */
	public enum FindHistotyType
	{
		/** 查询类型：只查询置顶的记录 */
		OnlyAlwaysTopRecords,
		/** 查询类型：只查询置未置顶的记录 */
		OnlyNotAlwaysTopRecords,
		/** 查询类型：查询所有记录 */
		IncludeAll
	}
}
